<?php
/*
|--------------------------------------------------------------------------
| Common function method
|--------------------------------------------------------------------------
*/

use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Database\Query\Builder as QueryBuilder;
use Hyperf\Database\Model\Builder as ModelBuilder;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Hyperf\Logger\LoggerFactory;
use Hyperf\Server\ServerFactory;
use Hyperf\Utils\ApplicationContext;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Http\Message\ServerRequestInterface;
use Swoole\Websocket\Frame;
use Swoole\WebSocket\Server as WebSocketServer;
use Hyperf\Utils\Str;
use Hyperf\Redis\Redis;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use App\Support\SendEmail;

/**
 * 容器实例
 *
 * @return ContainerInterface
 */
function di(): ContainerInterface
{
    return ApplicationContext::getContainer();
}

/**
 * Redis 客户端实例
 *
 * @return Redis|mixed
 */
function redis()
{
    return di()->get(Redis::class);
}

/**
 * Server 实例 基于 Swoole Server
 *
 * @return \Swoole\Coroutine\Server|\Swoole\Server
 */
function server()
{
    return di()->get(ServerFactory::class)->getServer()->getServer();
}

/**
 * WebSocket frame 实例
 *
 * @return mixed|Frame
 */
function frame()
{
    return di()->get(Frame::class);
}

/**
 * WebSocketServer 实例
 *
 * @return mixed|WebSocketServer
 */
function websocket()
{
    return di()->get(WebSocketServer::class);
}

/**
 * 缓存实例 简单的缓存
 *
 * @return mixed|\Psr\SimpleCache\CacheInterface
 */
function cache()
{
    return di()->get(Psr\SimpleCache\CacheInterface::class);
}

/**
 * Dispatch an event and call the listeners.
 *
 * @return mixed|\Psr\EventDispatcher\EventDispatcherInterface
 */
function event()
{
    return di()->get(Psr\EventDispatcher\EventDispatcherInterface::class);
}

/**
 * 控制台日志
 *
 * @return StdoutLoggerInterface|mixed
 */
function stdout_log()
{
    return di()->get(StdoutLoggerInterface::class);
}

/**
 * 文件日志
 *
 * @param string $name
 * @param string $group
 * @return LoggerInterface
 * @throws ContainerExceptionInterface
 * @throws NotFoundExceptionInterface
 */
function logger(string $name = 'APP', string $group = 'default'): LoggerInterface
{
    return di()->get(LoggerFactory::class)->get($name, $group);
}

/**
 * Http 请求实例
 *
 * @return mixed|ServerRequestInterface
 */
function request()
{
    return di()->get(ServerRequestInterface::class);
}

/**
 * 请求响应
 *
 * @return ResponseInterface|mixed
 */
function response()
{
    return di()->get(ResponseInterface::class);
}


/**
 *
 *
 */
function email()
{
    return di()->get(SendEmail::class);
}

/**
 * 获取媒体文件url
 *
 * @param string $path 文件相对路径
 * @return string
 */
function get_media_url(string $path): string
{
    return sprintf('%s/%s', rtrim(config('domain.img_url'), '/'), ltrim($path, '/'));
}

/**
 * 随机生成图片名
 *
 * @param string $ext 图片后缀名
 * @param array $filesize 图片文件大小信息
 * @return string
 */
function create_image_name(string $ext, array $filesize): string
{
    return uniqid() . Str::random() . '_' . $filesize[0] . 'x' . $filesize[1] . '.' . $ext;
}

/**
 * 判断0或正整数
 *
 * @param string|int $value 验证字符串
 * @param bool $isZero 判断是否可为0
 * @return bool
 */
function check_int($value, $isZero = false): bool
{
    $reg = $isZero ? '/^[+]{0,1}(\d+)$/' : '/^[1-9]\d*$/';
    return is_numeric($value) && preg_match($reg, $value);
}

/**
 * 解析英文逗号',' 拼接的 ID 字符串
 *
 * @param string|int $ids 字符串(例如; "1,2,3,4,5,6")
 * @return array
 */
function parse_ids($ids): array
{
    return array_unique(explode(',', trim($ids)));
}

/**
 * 推送消息到 Redis 订阅中
 *
 * @param string $chan
 * @param string|array $message
 */
function push_redis_subscribe(string $chan, $message)
{
    redis()->publish($chan, is_string($message) ? $message : json_encode($message));
}

/**
 * 生成随机文件名
 *
 * @param string $ext 文件后缀名
 * @return string
 */
function create_random_filename(string $ext): string
{
    $ext = $ext ? '.' . $ext : '';
    return Str::random(10) . uniqid() . $ext;
}

/**
 * 获取客户端你真实IP
 *
 * @return mixed|string
 */
function get_real_ip(): string
{
    if ($ip = request()->getHeaderLine('x-real-ip')) {
        return $ip;
    } else if ($ip = request()->getHeaderLine('x-forwarded-for')) {
        return $ip;
    }

    return request()->getServerParams()['remote_addr'] ?? '';
}


/**
 *
 * @return string
 */
function getLang(): string
{
    $lang = request()->getHeaderLine('lang');
    if (empty($lang)) {
        $lang = 'en';
    }
    return $lang;
}


function getPlatform(): string
{
    $platform = request()->getHeaderLine('platform');
    if (empty($platform)) {
        $platform = 1;
    }
    return $platform;
}


/**
 * 通过查询构造器读取分页数据
 *
 * @param QueryBuilder|ModelBuilder $model 查询构造器
 * @param array $fields 查询字段
 * @param int $page 当前分页
 * @param int $size 分页大小
 * @return array
 */
function toPaginate($model, array $fields = ['*'], int $page = 1, int $size = 15): array
{
    $total = $model->count();

    $data = [
        'rows' => [],
        'paginate' => [
            'page' => $page,
            'size' => $size,
            'total' => $total,
        ]
    ];

    if ($total > 0) $data['rows'] = $model->forPage($page, $size)->get($fields)->toArray();

    if ($data['rows'] && $model instanceof QueryBuilder) {
        foreach ($data['rows'] as &$row) {
            $row = (array)$row;
        }
    }

    return $data;
}


/**
 * 分页
 * @param $model
 * @param int $page
 * @param int $size
 * @return array
 */
function page($model, int $page = 0, int $size = 0): array
{
    $pageInfo = request()->getQueryParams();
    if (empty($page)) {
        if (isExistence($pageInfo, ['page'])) {
            $page = $pageInfo['page'];
        } else {
            $page = 1;
        }

    }
    if (empty($size)) {
        $size = 10;
        if (isExistence($pageInfo, ['size'])) {
            $size = $pageInfo['size'];
        }
        if (isExistence($pageInfo, ['limit'])) {
            $size = $pageInfo['limit'];
        }
    }
    $list = $model->paginate($size, ["*"], 'page', $page)->toArray();

    if (empty($list['data'])) {
        $lists = null;
    } else {
        $lists = $list['data'];
    }

    return [
        'current_page' => $list['current_page'] ?? 0,
        'last_page' => $list['last_page'] ?? 0,
        'total' => $list['total'] ?? 0,
        'list' => $lists,
    ];
}


/**
 * 记录代码抛出的错误
 * @param string $name 错误名称
 * @param string $error 错误详情
 * @return bool
 */
function recordExceptionLog(string $name, string $error): bool
{
    $ip = get_real_ip();
    $data = [
        'ip' => $ip,
        'content' => $error,
        'name' => $name,
        'create_time' => time()
    ];
    try {
        $rel = \App\Model\ExceptionLog::insert($data);


    } catch (Exception $e) {

    }
    return true;

}

/**
 * 判断一个数组中是否存在 且不为空的值 可判断多个
 * @param $params
 * @param array $field
 * @return bool
 */
function isExistence($params, array $field): bool
{
    $flag = true;
    foreach ($field as $k => $v) {
        if (!isset($params[$v]) || $params[$v] == '' || is_null($params[$v])) {
            $flag = false;
            break;
        }
    }
    return $flag;
}


/**
 * 下划线转驼峰
 * @param array $array
 * @return array
 */
function transformCamelArray(array &$array): array
{
    foreach ($array as $k => $v) {
        if (is_array($v)) {
            transformCamelArray($v);
        }
        unset($array[$k]);
        if (is_string($k)) {
            $array[Str::camel($k)] = $v;
        } else {
            $array[$k] = $v;
        }
    }

    return $array;
}


/**
 * 把驼峰风格字段名转化为下划线风格
 * @param array $array
 * 获取转换后的属性
 *
 * @return array
 */
function transformSnakeArray(array &$array): array
{
    foreach ($array as $k => $v) {
        if (is_array($v)) {
            transformSnakeArray($v);
        }
        unset($array[$k]);

        if (is_string($k)) {
            $array[Str::snake($k)] = $v;
        } else {
            $array[$k] = $v;
        }
    }

    return $array;
}


function randomSign(): string
{
    return uniqid() . Str::random();
}


/**
 * 返回链接地址/图片相对路径
 * @param string $url
 * @return mixed|string
 */
function relativePath(string $url)
{

    $urlData = parse_url($url);
    return $urlData['path'] ?? '';
}

/**
 * 返回完整的img 地址
 * @param string $url
 * @return string
 */
function imgUrl(string $url): string
{
    return env('IMG_URL', 'https://img.seekingaround.com') . $url;
}


/**
 * @return string
 */

function createOrderNo(): string
{

    return date('ymd', time()) . substr(microtime(), 2, 3) . sprintf('%05d', rand(0, 99999));

}


/**
 * 返回用户个人信息
 * @param $uid
 * @return array
 */
function userInfo($uid): array
{

    $userInfo = redis()->hGetAll('im:user_' . $uid);

    if (empty($userInfo)) {

        $userInfo = \App\Model\User::find($uid);

        if (!is_null($userInfo)) {
            $userInfo = $userInfo->toArray();
            $userInfo['online'] = isOnline($uid);
            \redis()->hMSet('im:user_' . $uid, $userInfo);
        }

    }

    return $userInfo;


}

/**
 * 返回 反性别
 * @param $uid
 * @return int
 */
function gender($uid): int
{
    $gender = redis()->hGet('im:user_' . $uid, 'gender');
    if ($gender == false) {
        $gender = \App\Model\User::where(['id' => $uid])->value('gender');
        redis()->zAdd('im:user_' . $uid, ['gender' => $gender]);
    }

    return $gender == 1 ? 2 : 1;

}


/**
 * 判断用户是否在线
 * @param $uid
 */
function isOnline($uid): int
{
    $online = redis()->hGet('im:user_' . $uid, 'online');
    return $online == 1 ? 1 : 0;
}


/**
 * 判断是否是会员
 * @param $uid
 * @return bool
 */
function isMember($uid): bool
{
    $userInfo = userInfo($uid);

    if ($userInfo['member'] == 1) {//
        return true;
    } else {
        return false;
    }


}

/**
 * 根据经纬度计算距离 返回公里为单位
 * @param $lng1
 * @param $lat1
 * @param $lng2
 * @param $lat2
 * @return float|int
 */
function getDistance($lat1, $lng1, $lat2, $lng2)
{
    $EARTH_RADIUS = 6378.137;

    $radLat1 = rad($lat1);
    $radLat2 = rad($lat2);
    $a = $radLat1 - $radLat2;
    $b = rad($lng1) - rad($lng2);
    $s = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
    $s = $s * $EARTH_RADIUS;
    $s = round($s * 10000) / 10000;

    return $s;
}

function rad($d)
{
    return $d * M_PI / 180.0;
}


/**
 * 计算某一个金纬度 附近多少米的经纬度 范围
 * @param $lon
 * @param $lat
 * @param $raidus
 * @return array[]|float[][]|int[][]
 */
function getAround($lon, $lat, $raidus): array
{

    $PI = M_PI;

    $latitude = $lat;

    $longitude = $lon;

    $degree = (24901 * 1609) / 360.0;

    $raidusMile = $raidus;

    $dpmLat = 1 / $degree;

    $radiusLat = $dpmLat * $raidusMile;

    $minLat = $latitude - $radiusLat;

    $maxLat = $latitude + $radiusLat;

    $mpdLng = $degree * cos($latitude * ($PI / 180));

    $dpmLng = 1 / $mpdLng;

    $radiusLng = $dpmLng * $raidusMile;

    $minLng = $longitude - $radiusLng;

    $maxLng = $longitude + $radiusLng;


    return ['min' => [
        'lon' => $minLng,
        'lat' => $minLat,
    ], 'max' => [
        'lon' => $maxLng,
        'lat' => $maxLat,
    ]];

}


function getRandomString($len, $chars = null): string

{

    if (is_null($chars)) {

        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";

    }

    mt_srand(10000000 * (double)microtime());

    for ($i = 0, $str = '', $lc = strlen($chars) - 1; $i < $len; $i++) {

        $str .= $chars[mt_rand(0, $lc)];

    }

    return $str;

}


/**
 * 获取某个月的所有日期
 * @param $month
 * @param int $year
 * @return array
 */
function getMonthDays($month, $year = 0)
{

    if ($year == 0) {
        $year = date('Y');
    }
    $days = cal_days_in_month(CAL_GREGORIAN, $month, $year);
    $firstDay = strtotime($year . '-' . $month . '-01');
    $date = [];
    for ($i = 1; $i <= $days; $i++) {
        $dat = date('Y-m-d', $firstDay);
        $date[$dat] = $dat;
        $firstDay += 86400;
    }
    return $date;


}


function verifyReceipt($receiptData)
{
    $endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt'; // 沙盒环境验证地址
    //$endpoint = 'https://buy.itunes.apple.com/verifyReceipt'; // 正式环境验证地址
    $postData = json_encode(array('receipt-data' => $receiptData));
    $ch = curl_init($endpoint);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
    $response = curl_exec($ch);
    $errno = curl_errno($ch);
    $errmsg = curl_error($ch);
    curl_close($ch);
    if ($errno != 0) {
        throw new Exception('Curl error: ' . $errmsg);
    }
    $data = json_decode($response);
    if (!is_object($data) || !isset($data->status)) {
        throw new Exception('Invalid response data');
    }
    if ($data->status == 0) {
        // 验证通过，检查订阅信息
        $latestReceiptInfo = $data->latest_receipt_info;
        $inApp = $data->receipt->in_app;
        if (!empty($latestReceiptInfo)) {
            // 订阅存在，检查订阅状态
            $latestReceipt = $latestReceiptInfo[0];
            if ($latestReceipt->expires_date_ms > time() * 1000) {
                // 订阅有效
                $productId = $latestReceipt->product_id;
                $transactionId = $latestReceipt->transaction_id;
                // TODO: 处理有效订阅的逻辑
            } else {
                // 订阅已过期
                $latestExpiredReceiptInfo = $data->latest_expired_receipt_info;
                if (!empty($latestExpiredReceiptInfo)) {
                    // 检查订阅是否已经被取消
                    $latestExpiredReceipt = $latestExpiredReceiptInfo[0];
                    if ($latestExpiredReceipt->cancellation_date_ms > 0) {
                        // 订阅已被取消
                        // TODO: 处理订阅已被取消的逻辑
                    } else {
                        // 订阅已过期
                        // TODO: 处理订阅已过期的逻辑
                    }
                } else {
                    // 订阅已过期
                    // TODO: 处理订阅已过期的逻辑
                }
            }
        } else {
            // 订阅不存在
            // TODO: 处理订阅不存在的逻辑
        }
    } else {
        // 验证失败
        // TODO: 处理验证失败的逻辑
    }
}


/**
 * 解析前端的媒体类型  返回 1 图片 2 视频 3  音乐 默认返回1
 * @param $type
 * @return int
 */
function analysisMediaType($type)
{
    $mediaType = 1;

    switch ($type) {
        case 'image':
            $mediaType = 1;
            break;
        case "video":
            $mediaType = 2;
            break;
        case 'audio':
            $mediaType = 3;
            break;
    }
    return $mediaType;
}


function defaultHead()
{

    return env('SYS_HEAD', 'https://img.seekingaround.com/seekingaround/dev/2023-06-08/64813f826debaIx8RVQt0gNEkxr0J.png');
}


/**
 *redis模糊删除 慎用：请在不影响服务的情况下下使用
 * @param $pattern
 * @return int
 * @throws RedisException
 */
function redisScanDel($pattern)
{
    $keysList = [];
    while (true) {
        $keys = \redis()->scan($iterator, $pattern . '*');
        $keysList = array_merge($keysList, $keys ?? []);
        if ($iterator === 0) {//迭代结束，未找到匹配pattern的key
            break;
        }
        if ($iterator === null) {//"游标为null了，重置为0，继续扫描"
            $iterator = "0";
        }
    }
    $keysList = array_unique($keysList);
    \redis()->del($keysList);
    return 1;
}


function currencyPrefix($currency)
{

    $data = [
        'CNY' => '￥',
        'USD' => '$',
    ];


    return $data[$currency]??'$';


}


